package com.game.base.observe;

import android.annotation.SuppressLint;

import com.game.base.utils.log.Logger;
import com.trello.rxlifecycle2.android.ActivityEvent;
import com.trello.rxlifecycle2.android.FragmentEvent;
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;
import com.trello.rxlifecycle2.components.support.RxDialogFragment;

import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import io.reactivex.Flowable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * 消息处理中枢
 */
public class MsgMgr {

    private static MsgMgr instance = new MsgMgr();

    public static MsgMgr getInstance() {
        return instance;
    }

    // ------------------------------事件发送及监听--------------------------------

    private CompositeDisposable rxDisposable = new CompositeDisposable();//订阅中心
    private ConcurrentHashMap<PObserver, Disposable> observerDisposableMap = new ConcurrentHashMap<>();
    private ConcurrentHashMap<Class, PObserver> classObserverMap = new ConcurrentHashMap<>();

    /**
     * 抛出事件到主线程
     *
     * @param key   事件的key
     * @param value 事件的value
     */
    public void sendMsg(String key, Object value) {
        Msg msg = new Msg(key, value);
        Logger.d("------>" + msg.toString());
        RxBus.getInstance().post(msg);
    }

    public void sendMsg(String key) {
        sendMsg(key, null);
    }

    /**
     * 延迟发消息
     */
    public void sendMsgDelay(final String key, final Object value) {
        delay(() -> sendMsg(key, value), 200);
    }

    public void sendMsgDelay(final String key) {
        sendMsgDelay(key, null);
    }

    /**
     * 绑定所有类型的消息通知，具体类型根据PObserver#onMessage的key进行区分
     *
     * @param observer 事件回调
     */
    public void attach(final PObserver observer) {
        attach(observer, false);
    }

    public void attach(final PObserver observer, boolean isReLoad) {
        if (observer == null) {
            Logger.e("------>PObserver is null.");
            return;
        }

        if (!isReLoad) {//是否可以重复添加同一个类 ，在检测网络状态时，在添加时不需要移除之前的类
            //移除前一个添加的相同的类
            PObserver preObserver = classObserverMap.get(observer.getClass());
            if (preObserver != null) {
                detach(preObserver);
            }
        }

        Disposable disposable = RxBus.getInstance().toFlowable(Msg.class)
                .onBackpressureBuffer().subscribeOn(Schedulers.io())
                .observeOn(AndroidScheduler.mainThread())
                .subscribe(msg -> observer.onMessage(msg.getKey(), msg.getData()), Logger::printThrowable);

        if (rxDisposable.add(disposable)) {
            classObserverMap.put(observer.getClass(), observer);
            observerDisposableMap.put(observer, disposable);
            Logger.d("------>attach[" + observer.getClass() + "], attached-size[" + rxDisposable.size() + "]");
        }
    }

    /**
     * 解除绑定消息通知
     *
     * @param observer 监听
     */
    public void detach(PObserver observer) {
        if (observer == null) return;

        Disposable disposable = observerDisposableMap.remove(observer);
        if (disposable != null) {
            rxDisposable.remove(disposable);
            Logger.d("------>detach[" + observer.getClass() + "], attached-size[" + rxDisposable.size() + "]");
        }
        //删除释放注册的observer, 防止内存溢出
        classObserverMap.remove(observer.getClass());
    }

    /**
     * 清除所有已经attach的监听，防止内存溢出[谨慎调用]
     */
    public void clear() {
        if (!rxDisposable.isDisposed()) {
            Logger.d("------>clear");
            rxDisposable.clear();
        }
        //清除所有
        classObserverMap.clear();
    }

    // -----------------------------线程切换及任务处理--------------------------------

    /**
     * 抛出事件到子线程并执行
     * <p>
     * 必须在继承自 RxAppCompatActivity 类中调用
     */
    public void runOnChildThread(RxAppCompatActivity activity, final Runnable runnable) {
        try {
            Flowable.empty().observeOn(Schedulers.io())
                    .compose(activity.bindUntilEvent(ActivityEvent.DESTROY))
                    .subscribe(new Subscriber<Object>() {
                        @Override
                        public void onSubscribe(Subscription s) {
                        }

                        @Override
                        public void onNext(Object o) {
                        }

                        @Override
                        public void onError(Throwable t) {
                        }

                        @Override
                        public void onComplete() {
                            if (runnable != null) runnable.run();
                        }
                    });
        } catch (Exception e) {
            Logger.printThrowable(e);
        }
    }

    /**
     * 延迟执行任务，默认在主进程执行
     * <p>
     * 必须在继承自 RxAppCompatActivity 类中调用
     */
    public void delay(RxAppCompatActivity activity, Runnable runnable, long delayTime) {
        delay(activity, ActivityEvent.DESTROY, runnable, delayTime);
    }

    @SuppressLint("CheckResult")
    public void delay(RxAppCompatActivity activity, ActivityEvent event, Runnable runnable, long delayTime) {
        Flowable.timer(delayTime, TimeUnit.MILLISECONDS)
                .observeOn(AndroidScheduler.mainThread())
                .compose(activity.bindUntilEvent(event))
                .subscribe(aLong -> {
                    if (runnable != null) runnable.run();
                });
    }

    /**
     * 延迟执行任务，默认在主进程执行
     * <p>
     * 必须在继承自 RxDialogFragment 类中调用 v4包
     */
    public void delay(RxDialogFragment dialogFragment, Runnable runnable, long delayTime) {
        delay(dialogFragment, FragmentEvent.DESTROY, runnable, delayTime);
    }

    @SuppressLint("CheckResult")
    public void delay(RxDialogFragment dialogFragment, FragmentEvent event, Runnable runnable, long delayTime) {
        Flowable.timer(delayTime, TimeUnit.MILLISECONDS)
                .observeOn(AndroidScheduler.mainThread())
                .compose(dialogFragment.bindUntilEvent(event))
                .subscribe(aLong -> {
                    if (runnable != null) runnable.run();
                });
    }
    // =============================== 下列方法会造成内存泄漏 =======================

    /**
     * 抛出事件到ui线程并执行
     *
     * @param runnable 事件
     */
    public void runOnUiThread(final Runnable runnable) {
        Flowable.empty().observeOn(AndroidScheduler.mainThread()).subscribe(new Subscriber<Object>() {
            @Override
            public void onSubscribe(Subscription s) {
            }

            @Override
            public void onNext(Object o) {
            }

            @Override
            public void onError(Throwable t) {
            }

            @Override
            public void onComplete() {
                if (runnable != null) runnable.run();
            }
        });
    }

    /**
     * 抛出事件到子线程并执行
     *
     * @param runnable 事件
     */
    public void runOnChildThread(final Runnable runnable) {
        Flowable.empty().observeOn(Schedulers.io()).subscribe(new Subscriber<Object>() {
            @Override
            public void onSubscribe(Subscription s) {
            }

            @Override
            public void onNext(Object o) {
            }

            @Override
            public void onError(Throwable t) {
            }

            @Override
            public void onComplete() {
                if (runnable != null) runnable.run();
            }
        });
    }

    /**
     * 延迟执行任务，默认在主进程执行
     *
     * @param runnable  延迟执行的任务
     * @param delayTime 延迟时间，ms级
     */
    public void delay(Runnable runnable, long delayTime) {
        delay(runnable, delayTime, true);
    }

    /**
     * 延迟执行任务
     *
     * @param runnable   延迟执行的任务
     * @param delayTime  延迟时间，ms级
     * @param mainThread 是否在主进程执行
     */
    @SuppressLint("CheckResult")
    public void delay(final Runnable runnable, long delayTime, boolean mainThread) {
        Flowable.timer(delayTime, TimeUnit.MILLISECONDS)
                .observeOn(mainThread ? AndroidScheduler.mainThread() : Schedulers.io())
                .subscribe(aLong -> {
                    if (runnable != null) runnable.run();
                });
    }

}
